﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Learn.Web.MVC.Code.StaticCache
{
    public class StaticCacheHtml : IStaticCache
    {

        public static IStaticCache Instance { get; private set; }
        public string CachePath { get; set; }
        public int CacheSubDirs { get; set; }

        static StaticCacheHtml()
        {
            Instance = new StaticCacheHtml();
        }

        private StaticCacheHtml()
        {
            CachePath = HttpContext.Current.Server.MapPath("~/Content/Html.Cache");
            CacheSubDirs = 100;
        }

        private string GetCorrectKey(string key)
        {
            return key.Trim().ToLower();
        }

        private string GetCacheFileName(string key)
        {
            key = GetCorrectKey(key);

            var subPath = Math.Abs(key.GetHashCode()) % CacheSubDirs;
            var path = Path.Combine(CachePath, subPath.ToString());
            if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
            var md5 = new MD5CryptoServiceProvider().ComputeHash(ASCIIEncoding.ASCII.GetBytes(key));
            var sb = new StringBuilder();
            foreach (byte b in md5)
            {
                sb.Append(Convert.ToString(b));
            }

            //var fileName = ASCIIEncoding.ASCII.GetString(md5);

            return Path.Combine(path, sb.ToString() + ".html");
        }

        public void Set(string key, object value, DateTime absoluteExpiration)
        {
            #region
            var fileName = GetCacheFileName(key);
            using(var fileStream = new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.Write))
            {
                using (var writer = new StreamWriter(fileStream))
                {
                    writer.WriteLine(string.Format("{0}",absoluteExpiration.Ticks));

                    writer.WriteLine(string.Format("<!-- {0};{1} -->",fileName.Substring(CachePath.Length),absoluteExpiration));

                    writer.WriteLine(value);

                    writer.Flush();
                }
            }
            #endregion
        }

        public void Set(string key, object value, TimeSpan slidingExpirations)
        {
            Set(key,value,DateTime.Now.Add(slidingExpirations));
        }

        public T Get<T>(string key)
        {
            #region
            var fileName = GetCacheFileName(key);
            if (File.Exists(fileName))
            {
                using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                {
                    using (var reader = new StreamReader(stream))
                    {
                        var cacheTime = reader.ReadLine();
                        var expiration = Convert.ToInt64(cacheTime);
                        if (expiration > DateTime.Now.Ticks)
                        {
                            return (T)(object)reader.ReadToEnd();
                        }
                    }
                    File.Delete(fileName);
                }
            }
            return default(T);
            #endregion
        }

        public void Delete(string key)
        {
            File.Delete(GetCacheFileName(key));
        }
    }
}
